<?php
/**
 * 自动表单类
 * @author Tianfeng.Han
 * @package SwooleSystem
 *
 */
class Form extends Base
{
	public $vfjs_src = "/static/js/common.js?101012";
	public $field_types = array('text', 'textarea', 'radio', 'checkbox', 'select', 'password', 'hidden', 'city'); //允许的类型
	public $fields; //表单字段
	public $data; //表单数据
	const HASH_LENGTH = 15; //hash串长度
	
	/**
	 * 构造函数
	 * 
	 * @parms $formid 表单id
	 * @parms $fields 表单项
	 * @parms $usefile 允许传文件
	 *
	 * @return null
	 */
	function __construct($fields, $action="", $isMobile=0)
	{
		$this->fields = $fields;
		$this->action = $action;
		if($isMobile)
		{
			$this->vfjs_src = "/static/js/common2.js";
		}
	}

	/**
	 * 生成表单
	 *
	 * @param 出错时设置$error
	 * @return true or false
	 */
	function getForm($data='')
	{
		$forms = array();
		$notes = array();

		//formAction
		$forms['action'] = $this->action;

		foreach($this->fields as $fieldname => &$field)
		{
			$field['name'] = $fieldname;
			$field['id'] = $field['id'] ? $field['id'] : $field['name'];
			$method = 'parse_'.$field['type'];

			if(!in_array($field['type'], $this->field_types)){
				throw new MyException("表单控件".$fieldname."的type值错误，只支持[".join(",", $this->field_types)."]");
			}
			if(isset($field['equal']) && (!isset($field['notequal']))){
				throw new MyException("表单控件".$fieldname."指定equal属性也必须指定notequal");
			}
			if(isset($field['or']) && (isset($field['empty']) || isset($field['check']))){
				throw new MyException("表单控件".$fieldname."的属性or不能和empty,check共存");
			}

			//提示信息的id
			$noteid = "";
			if($field['options'] && ($field['type'] == 'radio' || $field['type'] == 'checkbox'))
			{
				$oid = array_keys($field['options']);
				$noteid = $field['id'].'_'.$oid[0];
			}
			else if($field['type'] == 'city')
			{
				$noteid = $field['id'].'_city';
				$field['options1'] = $this->com_areaModule->getProvince();
				$field['options2'] = $field['provid'] ? $this->com_areaModule->getCitysByProvid($field['provid']) : array();
			}
			else
			{
				$noteid = $field['id'];
			}

			$forms[$fieldname]['type'] = $field['type'];

			if($field['type'] != 'hidden')
			{
				if($field['dnote'])
				{
					$forms[$fieldname]['note'] = '<span id="'.$noteid.'_msg"><span class="cuowu">'.$field['dnote'].'</span></span>';
				}
				else
				{
					$forms[$fieldname]['note'] = '<span id="'.$noteid.'_msg"></span>';
				}
				$notes[$noteid] = $field['note'];
			}

			if($data[$fieldname] == '' && strpos($fieldname, '_') === false){
				$forms[$fieldname]['body'] = $this->$method($field);
			}else{
				$tmp = explode("_", $fieldname);
				if(count($tmp) < 2)
				{
					$forms[$fieldname]['body'] = $this->$method($field,$data[$fieldname]);
				}
				else
				{
					$forms[$fieldname]['body'] = $this->$method($field,$data[$tmp[0]][$tmp[1]]);
				}
			}
		}

		//formhash
		$formhash = 'a'.md5(uniqid()).md5(uniqid());

		//formhashJs
		$forms['formhashjs'] = $this->formhashJs($formhash, $notes);

		return $forms;
	}
	
	/**
	 * 验证表单
	 *
	 * @param 出错时设置$error
	 * @return true or false
	 */
	public function checkForm($input, $multi=1)
	{
		$result = array();

		//检查表单hash值
		$result['checkhash'] = $this->checkHash();

		//检查表单项
		$result += $this->checkInput($input, $error, $multi);

		//有错误
		if($error)
		{
			$str = '<script type="text/javascript" reload="1">';
			foreach($error as $id => $msg)
			{
				$str .= "error_handle_ajax('$id', '$msg');";
			}
			$str .= '</script>';
			echo Common::packXml($str);
			exit;
		}

		return $result;
	}

	/**
	 * 检查表单hash值的合法性
	 *
	 * @param 出错时设置$error
	 * @return true or false
	 */
	private function checkHash(){
		//校验串合法性比对
		if(strlen($_COOKIE['vcode']) != self::HASH_LENGTH){
			return 'N';
		}else{
			$vcode = str_replace($_COOKIE['vcode'] . '|', '', $_SESSION['vcode'], $count_session);
			if ($count_session != 1){
				return 'N';
			}
		}

		return "Y";
	}
	
	/**
	 * 检查表单项
	 *
	 * @param 出错时设置$error
	 * @return true or false
	 */
	private function checkInput($input, &$error, $multi)
	{
		$result = array();

		//逐个检查
		foreach($this->fields as $name => $f)
		{
			$value = $input[$name];

			//为空的情况
			if(isset($f['empty']) && ($f['type'] == 'city' ? empty($input[$name.'_city']) : empty($value)))
			{
				$error[$name] = $f['empty'];
				if(!$multi){
					return false;
				}
				continue;
			}

			//radio,select
			if(in_array($f['type'], array('radio','select'))){
				if(!in_array($value, array_keys($f['options']))){
					$error[$name] = $f['error'];
					if(!$multi){
						return false;
					}
				}
			}
			//检测两个值是否相等
			elseif(in_array($f['type'], array('text','password')) && isset($f['equal'])){
				if($value != $input[$f['equal']])
				{
					$error[$name] = $f['notequal'];
					if(!$multi){
						return false;
					}
				}
			}
			//city
			elseif($f['type'] == 'city')
			{
				if($input[$name.'_province'] != 0 && !preg_match("/^[1-9][0-9]00$/", $input[$name.'_province']) || $input[$name.'_city'] != 0 && !preg_match("/^[1-9][0-9]{3}$/", $input[$name.'_city']))
				{
					$error[$name.'_city'] = $f['error'];
					if(!$multi){
						return false;
					}
				}
				$result[$name.'_province'] = $input[$name.'_province'];
				$result[$name.'_city'] = $input[$name.'_city'];
			}
			//检测字符串
			elseif(!empty($value) && isset($f['check'])){
				$chk_arr = explode(",", $f['check']);
				
				//规则检查
				$rg_arr = explode("|", $chk_arr[0]);
				if($rg_arr){
					foreach($rg_arr as $chkname)
					{
						$regular = Check::getRegular($chkname);
						if(!preg_match($regular, $value)){
							$error[$name] = $f['error'];
							break;
						}
					}
				}

				//长度检查
				if($chk_arr[1])
				{
					$len_arr = explode("-", $chk_arr[1]);
					$mb_len = mb_strlen($value, 'utf-8');
					if(count($len_arr) == 1)
					{
						if($mb_len > $len_arr[0])
						{
							$error[$name] = $f['error'];
						}
					}
					else
					{
						if($mb_len < $len_arr[0] || $mb_len > $len_arr[1])
						{
							$error[$name] = $f['error'];
						}
					}
				}

				if(!$multi){
					return false;
				}
			}

			$result[$name] = $value;
		}

		//有错误
		if($multi && !empty($error))
		{
			return array();
		}

		return $result;
	}
	
	function formhashJs($formhash, $notes)
	{
		Common::load("JavaScriptPacker");
		$jsStr = "";
		$jsStr .= '<script type="text/javascript">';
		$jsStr .= 'var noteArr = new Array();';
		if($notes){
			foreach($notes as $key => $val){
				$jsStr .= 'noteArr["'.$key.'"] = "'.$val.'";';
			}
		}
		$jsStr .= '</script>';
		$jsStr .= '<script language="JavaScript" src="'.$this->vfjs_src.'" type="text/javascript"></script>'."\n";
		$jsStr .= '<script type="text/javascript">';

		$jsStr .= 'init_form("@FormID");';
		$jsTmp .= 'function hsform(obj){';
		$jsTmp .= 'var a="'.$formhash.'";';
		$jsTmp .= 'var c="'.$this->getrand($formhash, self::HASH_LENGTH).'";';
		$jsTmp .= 'var b=a.substr(c,'.self::HASH_LENGTH.');
var dl=new Date();
dl.setDate(dl.getDate()+60);
document.cookie="vcode=" +b+";expires="+dl.toGMTString();';
		$jsTmp .= 'if(checkform(obj)){obj.submit();}return false;';
		$jsTmp .= '}';
		$packer = new JavaScriptPacker($jsTmp, 'Normal', true, false);
		#$jsStr .= $packer->pack();
		$jsStr .= $jsTmp;

		$jsStr .= '</script>';

		return $jsStr;
	}

	/**
	 * 生成hash值，并返回vcode的起始位
	 * 
	 * 无需empty，可选check
	 *
	 */
	private function getrand($hashkey, $length)
	{
		$max = strlen($hashkey) - $length;
		$start = rand(0, $max);

		$vcode = (isset($_SESSION['vcode'])) ? $_SESSION['vcode'] : ''; 
		$vcode = substr($hashkey, $start, $length) . '|' . $vcode;
		//大于5个把最后一个去掉
		if (strlen($vcode) > (self::HASH_LENGTH+1) * 5)
		{
			$vcode = substr($vcode, 0, strlen($vcode) - self::HASH_LENGTH - 1);
		}
		$_SESSION['vcode'] = $vcode;

		return $start;
	}

	/**
	 * 隐藏域
	 * 
	 * 无需empty，可选check
	 *
	 */
	function parse_hidden($params,$value='')
	{
		$value = $value ? $value : $params['value'];
		$form = "<input type='hidden' name='{$params['name']}' id='{$params['id']}' value='$value' />";
		return $form;
	}

	/**
	 * 密码域
	 * 
	 * 可选check，empty
	 *
	 */
	function parse_password($params,$value='')
	{
		$attr = array();
		$attr[] = 'type="password"';
		$attr[] = 'name="'.$params['name'].'"';
		$attr[] = 'id="'.$params['id'].'"';
		if($params['value']) $attr[] = 'value="'.$params['value'].'"';
		if($params['check']) $attr[] = 'check="'.$params['check'].'"';
		if($params['empty']) $attr[] = 'empty="'.$params['empty'].'"';
		if($params['error']) $attr[] = 'error="'.$params['error'].'"';
		if($params['equal']) $attr[] = 'equal="'.$params['equal'].'"';
		if($params['notequal']) $attr[] = 'notequal="'.$params['notequal'].'"';

		return "<input ".join(" ", $attr)." />";
	}
	
	/**
	 * 文本框
	 * 
	 * 必须有check，可选empty,ajax
	 *
	 */
	function parse_text($params,$value='')
	{
		if(!$params['check']){
			throw new MyException("表单text类型必须有check属性");
		}

		$attr = array();
		$attr[] = 'type="text"';
		$attr[] = 'name="'.$params['name'].'"';
		$attr[] = 'id="'.$params['id'].'"';
		if($value != '') $attr[] = 'value="'.$value.'"';
		if($params['class']) $attr[] = 'class="'.$params['class'].'"';
		if($params['empty']) $attr[] = 'empty="'.$params['empty'].'"';
		if($params['check']) $attr[] = 'check="'.$params['check'].'"';
		if($params['error']) $attr[] = 'error="'.$params['error'].'"';
		if($params['ajax']) $attr[] = 'ajax="'.$params['ajax'].'"';
		if($params['equal']) $attr[] = 'equal="'.$params['equal'].'"';
		if($params['notequal']) $attr[] = 'notequal="'.$params['notequal'].'"';

		return "<input ".join(" ", $attr)." />";
	}
	
	/**
	 * 文本区域
	 * 
	 * 必须有check，可选empty,ajax
	 *
	 */
	function parse_textarea($params,$value='')
	{
		if(!$params['check']){
			throw new MyException("表单textarea类型必须有check属性");
		}

		$attr = array();
		$attr[] = 'name="'.$params['name'].'"';
		$attr[] = 'id="'.$params['id'].'"';
		if($params['class']) $attr[] = 'class="'.$params['class'].'"';
		if($params['empty']) $attr[] = 'empty="'.$params['empty'].'"';
		if($params['check']) $attr[] = 'check="'.$params['check'].'"';
		if($params['error']) $attr[] = 'error="'.$params['error'].'"';

		return "<textarea ".join(" ", $attr)." />$value</textarea>";
	}

	/**
	 * 单选框
	 * 
	 * 无需check，可选empty
	 *
	 */
	function parse_radio($params,$value='')
	{
		$htmlStr = "";
		$foreach_count = 0;

		$attr = array();
		$attr[] = 'type="radio"';
		$attr[] = 'name="'.$params['name'].'"';
		if($params['class']) $attr[] = 'class="'.$params['class'].'"';
		if($params['empty']) $attr[] = 'empty="'.$params['empty'].'"';

		$value = preg_replace("/[0-9]+_/","",$value);

		if($params['options'])
		foreach($params['options'] as $k => $v)
		{
			if ($k == $value)
			{
				$htmlStr .= "<label><input ".join(" ", $attr)." id=\"{$params[name]}_{$k}\" value=\"$k\" checked=\"checked\" />".$v."</label>";
			}
			else
			{
				$htmlStr .= "<label><input ".join(" ", $attr)." id=\"{$params[name]}_{$k}\" value=\"$k\" />".$v."</label>";
			}

			if($params['br'] && $k % $params['br'] == 0)
			{
				$htmlStr .= "<br />";
			}
			if($foreach_count++ == 0)
			{
				array_pop($attr);
			}
		}

		return $htmlStr;
	}

	/**
	 * 复选框
	 * 
	 * 无需check，可选empty
	 *
	 */
	function parse_checkbox($params,$value='')
	{
		$htmlStr = "";
		$foreach_count = 0;

		$attr = array();
		$attr[] = 'type="checkbox"';
		$attr[] = 'name="'.$params['name'].'[]"';
		if($params['class']) $attr[] = 'class="'.$params['class'].'"';
		if($params['empty']) $attr[] = 'empty="'.$params['empty'].'"';

		$name = $params['name'];

		$value = explode(",", preg_replace("/[0-9]+_/","",$value));
		$value = is_array($value) ? $value : array($value);

		if($params['options'])
		foreach($params['options'] as $k => $v)
		{
			if(in_array($k, $value))
			{
				$htmlStr .= "<label><input ".join(" ", $attr)." id=\"{$name}_{$k}\" value=\"$k\" checked=\"checked\" />".$v.'</label>';
			}
			else
			{
				$htmlStr .= "<label><input ".join(" ", $attr)." id=\"{$name}_{$k}\" value=\"$k\" />".$v.'</label>';
			}
		}
		return $htmlStr;
	}

	/**
	 * 列表框
	 * 
	 * 无需check，可选empty
	 *
	 */
	function parse_select($params,$value='')
	{
		$attr = array();
		$attr[] = 'name="'.$params['name'].'"';
		$attr[] = 'id="'.$params['id'].'"';
		if($params['class']) $attr[] = 'class="'.$params['class'].'"';
		if($params['empty']) $attr[] = 'empty="'.$params['empty'].'"';

		$htmlStr = $params['cstyle'] ? '<span class="cselect">' : '';
		$htmlStr .= "<select ".join(" ", $attr).">\n";
		$htmlStr .= "<option value=\"0\">-- 请选择 --</option>\n";
		
		if($params['options'])
		foreach($params['options'] as $k => $v)
		{
			if ($k == $value)
			{
				$htmlStr .= "<option value=\"{$k}\" selected=\"selected\">{$v}</option>\n";
			}
			else
			{
				$htmlStr .= "<option value=\"{$k}\">{$v}</option>\n";
			}
		}

		$htmlStr .= $params['cstyle'] ? "</select><i class=\"arrow\"></i></span>\n" : "</select>\n";

		return $htmlStr;
	}

	/**
	 * 省市联动
	 * 
	 * 无需check，可选empty
	 *
	 */
	function parse_city($params,$value1='',$value2='')
	{
		$attr = array();
		$attr['name'] = 'name="'.$params['name'].'_province"';
		$attr['id'] = 'id="'.$params['name'].'_province"';
		if($params['class']) $attr['class'] = 'class="'.$params['class'].'"';

		$htmlStr = $params['cstyle'] ? '<span class="cselect">' : '';

		//省
		$htmlStr .= "<select ".join(" ", $attr)." onChange=\"showcity('".$params['name']."', this.value)\">\n";
		$htmlStr .= "<option value=\"0\">--省份--</option>\n";
		
		if($params['options1'])
		foreach($params['options1'] as $k => $v)
		{
			if ($k == $value1)
			{
				$htmlStr .= "<option value=\"{$k}\" selected=\"selected\">{$v}</option>\n";
			}
			else
			{
				$htmlStr .= "<option value=\"{$k}\">{$v}</option>\n";
			}
		}
		$htmlStr .= $params['cstyle'] ? "</select><i class=\"arrow\"></i></span>\n" : "</select>\n";

		//市
		$attr['name'] = 'name="'.$params['name'].'_city"';
		$attr['id'] = 'id="'.$params['name'].'_city"';
		if($params['empty']) $attr['empty'] = 'empty="'.$params['empty'].'"';

		$htmlStr .= $params['cstyle'] ? '<span class="cselect">' : '';
		$htmlStr .= "<select ".join(" ", $attr).">\n";
		$htmlStr .= "<option value=\"0\">--城市--</option>\n";
		
		if($params['options2'])
		foreach($params['options2'] as $k => $v)
		{
			if ($k == $value2)
			{
				$htmlStr .= "<option value=\"{$k}\" selected=\"selected\">{$v}</option>\n";
			}
			else
			{
				$htmlStr .= "<option value=\"{$k}\">{$v}</option>\n";
			}
		}
		$htmlStr .= $params['cstyle'] ? "</select><i class=\"arrow\"></i></span>\n" : "</select>\n";

		return $htmlStr;
	}
}
?>